package renai

import (
	"database/sql"
	"gitee.com/tziye/renai-go/common"
	"gitee.com/tziye/renai-go/sqlproxy"
	"github.com/go-sql-driver/mysql"
	"github.com/google/uuid"
	"time"
)

const (
	Live = "Live"
	Dead = "Dead"
)

type RenaiDataSource struct {
	Url      string
	Database string
	User     string
	Password string
	Params   map[string]string
}

type InstanceSpec struct {
	Group   string
	Service string
	Port    int
}

type RenaiInstance struct {
	Id         int64
	InstanceId string
	G          string
	Service    string
	Ip         string
	Port       int
	Status     string
	Leader     bool
	CreateTime time.Time
	UpdateTime time.Time
}

type RenaiClient struct {
	Instance       RenaiInstance
	EnableRegister bool
	Db             *sql.DB
}

func NewRenaiClient(dataSource RenaiDataSource, spec InstanceSpec) *RenaiClient {
	params := make(map[string]string)
	params["parseTime"] = "true"
	params["loc"] = "Local"
	if dataSource.Params != nil {
		for k, v := range dataSource.Params {
			params[k] = v
		}
	}
	cfg := mysql.Config{
		User:   dataSource.User,
		Passwd: dataSource.Password,
		Net:    "tcp",
		Addr:   dataSource.Url,
		DBName: dataSource.Database,
		Params: params,
	}
	db, err := sql.Open("mysql", cfg.FormatDSN())
	db.SetMaxIdleConns(5)
	db.SetMaxOpenConns(10)
	common.PanicErr(err)
	instance := RenaiInstance{
		InstanceId: uuid.New().String(),
		G:          spec.Group,
		Service:    spec.Service,
		Ip:         common.LocalIp(),
		Port:       spec.Port,
	}
	renaiClient := RenaiClient{Instance: instance, EnableRegister: true, Db: db}
	renaiClient.register()
	return &renaiClient
}

func (renaiClient *RenaiClient) register() {
	ticker := time.NewTicker(5 * time.Second)
	go func() {
		for {
			select {
			case <-ticker.C:
				if renaiClient.EnableRegister {
					s := `INSERT INTO renai_instance(instance_id, g, service, ip, port, status) VALUES (?, ?, ?, ?, ?, ?) 
							ON DUPLICATE KEY UPDATE g = VALUES(g), service = VALUES(service), ip = VALUES(ip), 
							                        port = VALUES(port), status = VALUES(status), update_time = NOW()`
					instance := renaiClient.Instance
					sqlproxy.Exec(renaiClient.Db, s, instance.InstanceId, instance.G, instance.Service, instance.Ip, instance.Port, Live)
				}
			}
		}
	}()
}

func (renaiClient *RenaiClient) RealStatus() RenaiInstance {
	s := "SELECT * FROM renai_instance WHERE instance_id = ?"
	row := sqlproxy.QueryRow(renaiClient.Db, s, renaiClient.Instance.InstanceId)
	var instance RenaiInstance
	err := row.Scan(&instance.Id, &instance.InstanceId, &instance.G, &instance.Service, &instance.Ip,
		&instance.Port, &instance.Status, &instance.Leader, &instance.CreateTime, &instance.UpdateTime)
	common.LogErr(err)
	return instance
}

func (renaiClient *RenaiClient) Instances() []RenaiInstance {
	return renaiClient.LookUp(renaiClient.Instance.G, renaiClient.Instance.Service)
}

func (renaiClient *RenaiClient) LookUp(group string, service string) []RenaiInstance {
	s := "SELECT * FROM renai_instance_valid WHERE g = ? AND service = ?"
	rows := sqlproxy.QueryRows(renaiClient.Db, s, group, service)
	defer rows.Close()
	var instances []RenaiInstance
	for rows.Next() {
		var instance RenaiInstance
		err := rows.Scan(&instance.Id, &instance.InstanceId, &instance.G, &instance.Service, &instance.Ip,
			&instance.Port, &instance.Status, &instance.Leader, &instance.CreateTime, &instance.UpdateTime)
		common.PanicErr(err)
		instances = append(instances, instance)
	}
	return instances
}

// Test

var TestKeys = []string{TestK1, TestK2, TestK3, TestK4, TestK5}

const (
	TestK1 = "KEY1"
	TestK2 = "KEY2"
	TestK3 = "KEY3"
	TestK4 = "KEY4"
	TestK5 = "KEY5"
)

func TestGenerateKey() string {
	key := uuid.New().String()
	TestKeys = append(TestKeys, key)
	return key
}

func TestSetup() *RenaiClient {
	dataSource := RenaiDataSource{
		Url:      "localhost:3306",
		Database: "renai",
		User:     "root",
		Password: "Ab_12345",
	}

	instance := InstanceSpec{
		Group:   "testG",
		Service: "testS",
	}

	return NewRenaiClient(dataSource, instance)
}
